<?php

use Godruoyi\Snowflake\Snowflake;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Illuminate\Support\Env;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Redis;

/**
 * 获取错误信息
 * @param $code
 * @return mixed
 */
function getMsg($code)
{
    return config('error')[(int)$code];
}


function set_config($dotKey, $value)
{
    return (new \App\Models\ConfigModel())->setConfig($dotKey, $value);
}

//时间格式化（时间戳）
function uc_time_ago($ptime)
{

    switch ($ptime) {
        //今天
        case date('Ymd', $ptime) == date('Ymd'):
            $msg = date('H:i', $ptime);
            break;
        //昨天
        case date('Ymd', $ptime) == date('Ymd', time() - 60 * 60 * 24):
            $msg = '昨天' . date('H:i', $ptime);;
            break;
        //今年
        case date('Y', $ptime) == date('Y'):
            $msg = date('m月d日H:i', $ptime);
            break;
        //其他
        default:
            $msg = date('Y年m月d日H:i', $ptime);
    }
    return $msg;
}

function save_cache($cacheKey, int $value = 0)
{
    if (Illuminate\Support\Facades\Cache::has($cacheKey)) {
        $arr = Illuminate\Support\Facades\Cache::get($cacheKey);
        if (!in_array($value, $arr['ids'])) {
            $passTime = time() - $arr['time'];
            !empty($value) && $arr['ids'][] = $value;
            $arr['time'] = time();
            $arr['ttl'] = $arr['ttl'];
            $arr['count'] += 1;
            Illuminate\Support\Facades\Cache::put($cacheKey, $arr, $arr['ttl'] - $passTime);
        }
    } else {
        $ttl = strtotime(date('Y-m-d', strtotime('+1 day'))) - time();
        $count = empty($value) ? 0 : 1;
        Illuminate\Support\Facades\Cache::put($cacheKey, ['time' => time(), 'ttl' => $ttl, 'ids' => [$value], 'count' => $count], $ttl);
    }
}

function save_cache_count($cacheKey, $value = 0)
{
    if (Illuminate\Support\Facades\Cache::has($cacheKey)) {
        $arr = Illuminate\Support\Facades\Cache::get($cacheKey);
        if (!in_array($value, $arr['ids'])) {
            !empty($value) && $arr['ids'][] = $value;
        }
        $passTime = time() - $arr['time'];
        $arr['time'] = time();
        $arr['ttl'] = $arr['ttl'];
        $arr['count'] += 1;
        Illuminate\Support\Facades\Cache::put($cacheKey, $arr, $arr['ttl'] - $passTime);
    } else {
        $ttl = strtotime(date('Y-m-d', strtotime('+1 day'))) - time();
        $count = empty($value) ? 0 : 1;
        Illuminate\Support\Facades\Cache::put($cacheKey, ['time' => time(), 'ttl' => $ttl, 'ids' => [$value], 'count' => $count], $ttl);
    }
}

function get_config_cache($name)
{

    if (Cache::has('cf_' . $name)) {
        return Cache::get('cf_' . $name);
    }
    //从config表中寻找
    $res = \App\Models\ConfigModel::where('name', $name)->first();
    if (empty($res)) {
        return false;
    }
    Cache::put('cf_' . $res->name, [
        'title' => $res->title,
        'name' => $res->name,
        'value' => $res->value,
        'description' => $res->description,
        'image' => $res->image,
        'type' => $res->type
    ], 3600 * 24 * 10);
    return Cache::get('cf_' . $name);

}


/**
 * 生成人物关系
 * @param $str  邀请人的人物关系
 * @param $id   邀请人的id
 * @param int $level 层级限制
 * @return false|string
 */
function generateRelation($str, $id, $level = 3)
{
    $str = sprintf('%s%d,', $str, $id);
    if (substr_count($str, ',') > ($level + 1)) {
        return substr($str, strpos($str, ',', 2));
    } elseif (strpos($str, ',') !== 0) {
        return sprintf(',%s', $str);
    } else {
        return $str;
    }
}


function upload_image($path, $file, $drive = 'oss')
{
    $disk = Storage::disk($drive);

    //将图片上传到OSS中，并返回图片路径信息 值如:avatar/WsH9mBklpAQUBQB4mL.jpeg
    $path = $disk->put($path, $file);

    //由于图片不在本地，所以我们应该获取图片的完整路径，
    //值如：https://test.oss-cn-hongkong.aliyuncs.com/avatar/8GdIcz1NaCZ.jpeg
    return $disk->url($path);
}

// 拼凑阿里oss域名前缀
function aliyun_domain()
{
    //return "https://swanshop.oss-ap-south-1.aliyuncs.com";
    //
    return env('ALIYUN_IS_CNAME') ?
        env('ALIYUN_CDN_DOMAIN') :
        sprintf('%s://%s.%s', env('ALIYUN_SSL') ? 'https' : 'http', env('ALIYUN_BUCKET'), env('ALIYUN_ENDPOINT'));
}

/**
 * 生成红包
 * @param $amount 金额
 * @param $number   数量
 * @param float $min 最少得多少
 * @return mixed
 */
function generateRedEnvelope($amount, $number, $min = 1)
{
    for ($i = 1; $i < $number; $i++) {
        $safe_total = ($amount - ($number - $i) * $min) / ($number - $i);
        $money = round(mt_rand($min * 100, $safe_total * 100) / 100);
        $amount = round($amount - $money);

        $arr[] = $money;
    }
    $arr[] = $amount;

    rsort($arr);
    $str = implode(',', $arr);
    return $str;
}


/**
 * 生成红包
 * @param $amount 金额
 * @param $number   数量
 * @param float $min 最少得多少
 * @return mixed
 */
function generateRedEnvelope2($amount, $number, $min = 1)
{

    for ($i = 1; $i < $number; $i++) {
        $safe_total = ($amount - ($number - $i) * $min) / ($number - $i);
        $money = mt_rand($min , $safe_total) ;
        $amount = round($amount - $money);

        $arr[] = $money;
    }
    $arr[] = $amount;

    rsort($arr);
    $str = implode(',', $arr);
    return $str;
}

/**
 * 根据奖品价值返回中奖数组
 * @param $amount
 * @param $number
 * @param $min
 * @param $prizes
 * @return array
 */
function generatePrize($amount, $number, $min, $prizes): array
{
    $arr = [];
    $rand_amount = $amount;

    if ($rand_amount == 0) {
        foreach ($prizes as $prize) {
            if ($prize->amount == $rand_amount) {
                $arr[] = $prize->amount . "." . $prize->id;
                break;
            }
        }

        return $arr;
    }


    for ($i = 0; $i < $number; $i++) {
        if ($i == $number - 1) {
            $money = round($amount);
            if ($money < 0) {
                $money = 0;
            }
        } else {
            $safe_total = ($rand_amount - ($number - $i) * $min) / ($number - $i);
            if ($safe_total <= $min * 100) {
                $safe_total = $min * 100;
            }
            $money = round(mt_rand($min * 100, $safe_total * 100) / 100);
            $rand_amount = round($amount - $money);
        }

        $prev = '';
        foreach ($prizes as $prize) {
            if ($prize->amount <= $money) {
                $prev = $prize->amount . "." . $prize->id;
            } else {
                break;
            }
        }
        if (!empty($prev)) {
            $arr[] = $prev;
        }
    }

    //sort($arr);
    shuffle($arr);  //20210919 石头修改为随机顺序
    return $arr;

}


/**
 * 取得根域名
 * @param string $domain 域名
 * @return string 返回根域名
 */
function GetUrlToDomain(string $domain): string
{
    $re_domain = '';
    $domain_postfix_cn_array = array("com", "net", "org", "gov", "edu", "com.cn", "cn", "in","top","buzz","asia","online","live","store","st","shop");
    $array_domain = explode(".", $domain);
    $array_num = count($array_domain) - 1;
    if (!$array_num) {
        $domain_arr = explode('://', $domain);
        return end($domain_arr);
    }
    if ($array_domain[$array_num] == 'cn') {
        if (in_array($array_domain[$array_num - 1], $domain_postfix_cn_array)) {
            $re_domain = $array_domain[$array_num - 2] . "." . $array_domain[$array_num - 1] . "." . $array_domain[$array_num];
        } else {
            $re_domain = $array_domain[$array_num - 1] . "." . $array_domain[$array_num];
        }
    } else {
        $re_domain = $array_domain[$array_num - 1] . "." . $array_domain[$array_num];
    }
    return $re_domain;
}

/**
 * 加解密
 * @param $string       string     需要加密的字符串
 * @param $operation    string     操作：E=加密,D=解密
 * @return bool|mixed|string
 */
function encryptAndDecrypt($string, $operation)
{
    $key = md5('base64:0GKbqBuC0KzL3tkw4krj0+6DmrIzJoVD7LD6uiHsIjc=');
    $key_length = strlen($key);
    $string = $operation == 'D' ? base64_decode($string) : substr(md5($string . $key), 0, 8) . $string;
    $string_length = strlen($string);
    $rndkey = $box = array();
    $result = '';
    for ($i = 0; $i <= 255; $i++) {
        $rndkey[$i] = ord($key[$i % $key_length]);
        $box[$i] = $i;
    }
    for ($j = $i = 0; $i < 256; $i++) {
        $j = ($j + $box[$i] + $rndkey[$i]) % 256;
        $tmp = $box[$i];
        $box[$i] = $box[$j];
        $box[$j] = $tmp;
    }
    for ($a = $j = $i = 0; $i < $string_length; $i++) {
        $a = ($a + 1) % 256;
        $j = ($j + $box[$a]) % 256;
        $tmp = $box[$a];
        $box[$a] = $box[$j];
        $box[$j] = $tmp;
        $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
    }
    if ($operation == 'D') {
        return substr($result, 0, 8) == substr(md5(substr($result, 8) . $key), 0, 8) ? substr($result, 8) : '';
    } else {
        return str_replace('=', '', base64_encode($result));
    }
}

/**
 * 递归实现无限极分类
 * @param  $data          array 分类数据
 * @param  $parent_id     int   父ID
 * @param  $level         int   分类级别
 * @return array 分好类的数组 直接遍历即可 $level可以用来遍历缩进
 */
function getTree($data, $parent_id = 0, $level = 0)
{
    //声明静态数组,避免递归调用时,多次声明导致数组覆盖
    static $list = [];
    foreach ($data as $key => $value) {
        //第一次遍历,找到父节点为根节点的节点 也就是pid=0的节点
        if ($value['parent_id'] == $parent_id) {
            //父节点为根节点的节点,级别为0，也就是第一级
            $value['level'] = $level;
            //把数组放到list中
            $list[] = $value;
            //把这个节点从数组中移除,减少后续递归消耗
            unset($data[$key]);
            //开始递归,查找父ID为该节点ID的节点,级别则为原级别+1
            getTree($data, $value['id'], $level + 1);
        }
    }
    return $list;
}


/**
 * 获取指定层级的下级数据
 * @param array $data 分类数据
 * @param int $parent_id 上级id
 * @param int $end_level 结束层级(基于上级id对应的起始层级，如：$start_level = 1,$end_level = 2查询2层下级；$start_level = 2,$end_level = 2查询1层下级)
 * @param int $start_level 下级的起始层级，默认为1，
 * @return array
 */
function getLevelChild(array $data, int $parent_id, int $end_level, int $start_level = 1)
{
    //声明静态数组,避免递归调用时,多次声明导致数组覆盖
    static $list = [];
    foreach ($data as $key => $value) {
        //第一次遍历,找到父节点为根节点的节点 也就是pid=0的节点
        if ($value['parent_id'] == $parent_id && $end_level >= $start_level) {
            //父节点为根节点的节点,级别为0，也就是第一级
            $value['level'] = $start_level;
            //把数组放到list中
            $list[] = $value;
            //把这个节点从数组中移除,减少后续递归消耗
            unset($data[$key]);
            //开始递归,查找父ID为该节点ID的节点,级别则为原级别+1
            getLevelChild($data, $value['id'], $end_level, $start_level + 1);
        }
    }
    return $list;
}

/*
* 通过下级，找到他的所有上级
*/
function getParents($data, $id)
{
    $arr = array();
    foreach ($data as $v) {
        if ($v['id'] == $id) {
            $arr[] = $v;
            $arr = array_merge(getParents($data, $v['parent_id']), $arr);
        }
    }
    return $arr;
}

/*
*通过传入的父ID的，找到他的所有下级的ID
*/
function getChildsId($cate, $pid)
{
    $arr = array();
    foreach ($cate as $v) {
        if (gettype($v) == 'object') {
            $v = object_to_array($v);
        }
        if ($v['parent_id'] == $pid) {
            $arr[] = $v['id'];
            $arr = array_merge($arr, getChildsId($cate, $v['id']));
        }
    }
    return $arr;
}


/**
 * 签名算法
 * @param array $data
 * @param string $secret_key
 * @return string
 */
function sign(array $data, string $secret_key)
{
    unset($data['sign']);
    ksort($data);
    $str = '';
    foreach ($data as $key => $value) {
        $str .= $key . '=' . $value . '&';
    }
    $str .= 'key=' . $secret_key;
    $sign = md5($str);
    $sign = strtoupper($sign);
    return $sign;
}

/**
 * 签名验证
 * 签名生成方式：1.对参数按键名进行升序排序
 *             2.对排序后的参数进行key=value&key=value 形式进行拼接
 *             3.在结尾拼接上 key={secret}
 *             4.对拼接后的字符串进行hash加密，加密的hash算法使用 sha1
 * @param array $param
 * @return bool
 */
function checkSign(array $param)
{
    //\Log::channel('wslog')->info('验签参数为:' . print_r($param, true));

    if (!isset($param['now']) || !isset($param['random_str']) || !isset($param['signature'])) {
        return false;
    }
    //签名生命期判断（允许5分钟时间差） 随机字符串是否已经被使用，一个随机字符串5分钟内只允许使用一次
    if (time() - $param['now'] > 60 * 5) {
        //\Log::channel('wslog')->info('订单验签失败:now过期');
        return false;
    }

    if (redis::exists($param['random_str'])) {
        //\Log::channel('wslog')->info('订单验签失败:random_str已存在');
        return false;
    }
    Redis::setex($param['random_str'], 5 * 60, '1');//5分钟过期
    $signature = $param['signature'];
    unset($param['signature']);
    $buff = "";
    ksort($param);
    foreach ($param as $k => $v) {
        $buff .= $k . "=" . trim($v) . "&";
    }
    $string = rtrim($buff, '&');
    $key = config('laravels.sign_secret');
    $string .= '&key=' . $key;

    $sign = hash_hmac('sha1', $string, $key);
    if ($signature != $sign) {
        //\Log::channel('wslog')->info('订单验签失败:两端签名不一致');
        //\Log::channel('wslog')->info("服务端签名为:{$sign}");
        //\Log::channel('wslog')->info("客户端签名为:{$signature}");
        return false;
    }
    return $signature == $sign ? true : false;
}

//给一个字符串中间部分打码,
function markStr(?string $mobile, int $length = 4, string $mark = '*'): string
{
    $mobile = mb_str_split($mobile, $length);
    $count = count($mobile);
    if ($count == 0) { //空字符串或者null
        //return '';
        return str_pad('', $length, $mark);
    } elseif ($count == 1) { //$mobile字符串长度不足$length
        return str_pad('', mb_strlen($mobile[0]), $mark);
    } elseif ($count == 2) {
        $mobile[1] = str_pad('', mb_strlen($mobile[1]), $mark);
        return implode('', $mobile);
    } else {
        $mobile[1] = str_pad('', $length, $mark);
        return implode('', $mobile);
    }
}

//获取唯一码
function only_code()
{
    $no = (new Snowflake())->id();
    $lock = Cache::lock('snowflake_' . $no, 1);
    if ($lock->get()) {
        return $no;
    }
    return only_code();
}

//对象转数组
function objectToArray($array)
{
    if (is_object($array)) {
        $array = (array)$array;
    }
    if (is_array($array)) {
        foreach ($array as $key => $value) {
            $array[$key] = objectToArray($value);
        }
    }
    return $array;
}

//获取用户真实 ip_api
function getIp()
{
    if (getenv('HTTP_CLIENT_IP')) {
        $ip = getenv('HTTP_CLIENT_IP');
    } elseif (getenv('HTTP_X_REAL_IP')) {
        $ip = getenv('HTTP_X_REAL_IP');
    } elseif (getenv('HTTP_X_FORWARDED_FOR')) {
        $ip = getenv('HTTP_X_FORWARDED_FOR');
        $ips = explode(',', $ip);
        $ip = $ips[0];
    } elseif (getenv('REMOTE_ADDR')) {
        $ip = getenv('REMOTE_ADDR');
    } else {
        $ip = '0.0.0.0';
    }

    return $ip;
}

function geiIpWeb(){
    if(isset($_SERVER['HTTP_X_SHOPIFY_CLIENT_IP'])){
        $ip = $_SERVER['HTTP_X_SHOPIFY_CLIENT_IP'];
    }else if (isset($_SERVER['HTTP_CF_CONNECTING_IP'])) {
        $ip = $_SERVER['HTTP_CF_CONNECTING_IP'];
    } else {
        if (getenv('HTTP_CLIENT_IP') && strcasecmp(getenv('HTTP_CLIENT_IP'), 'unknown')) {
            $ip = getenv('HTTP_CLIENT_IP');
        } elseif (getenv('HTTP_X_FORWARDED_FOR') && strcasecmp(getenv('HTTP_X_FORWARDED_FOR'), 'unknown')) {
            $ip = getenv('HTTP_X_FORWARDED_FOR');
        } elseif (getenv('REMOTE_ADDR') && strcasecmp(getenv('REMOTE_ADDR'), 'unknown')) {
            $ip = getenv('REMOTE_ADDR');
        } elseif (isset($_SERVER['REMOTE_ADDR']) && $_SERVER['REMOTE_ADDR'] && strcasecmp($_SERVER['REMOTE_ADDR'], 'unknown')) {
            $ip = $_SERVER['REMOTE_ADDR'];
        }
    }
    $ip_arr=explode(',',$ip);
    return $ip_arr[0];
}

function get_all_duration(): int
{
    $end = (int)(microtime(true) * 1000);
    $start = (int)((request()->server('REQUEST_TIME_FLOAT')) * 1000);
    return $end - $start;
}

function changeNumberStringZero($str): string
{
    $str = (string)$str;
    return preg_replace('/[.]$/', '', preg_replace('/0+$/', '', $str));
}

//取二进制数$num的第$bit位
function getBit($num, $bit)
{
    return $num >> $bit & 1;
}

if (!function_exists('isTronAddress')) {
    /**
     * 检查地址是不是波场地址
     *
     * @param $address
     *
     * @return bool
     */
    function isTronAddress($address): bool
    {
        $tron = new \IEXBase\TronAPI\Tron();
        try {
            $hex = $tron->toHex($address);
        } catch (Exception $e) {
            return false;
        }
        if (substr($hex, 0, 2) == '41' && strlen($hex) == 42) {
            return true;
        }

        return false;
    }
}
//获取随机字符串
function getrandstr($length)
{
    $str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890';
    $randStr = str_shuffle($str);//打乱字符串
    $rands = substr($randStr, 0, $length);//substr(string,start,length);返回字符串的一部分
    return $rands;
}
//获取随机字符串
function getrandstr_invite_code($length)
{
    $str = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ23456789';
    $randStr = str_shuffle($str);//打乱字符串
    $rands = substr($randStr, 0, $length);//substr(string,start,length);返回字符串的一部分
    return $rands;
}
//获取域名随机字符串
function getrandstrweb($length)
{
    $str = 'abcdefghijklmnopqrstuvwxyz123456789';
    $randStr = str_shuffle($str);//打乱字符串
    $rands = substr($randStr, 0, $length);//substr(string,start,length);返回字符串的一部分
    return $rands;
}


/**
 * 生成邀请奖励金额
 * @param $amount 金额
 * @param $number   数量
 * @param float $min 最少得多少
 * @return mixed
 */
function invitationReward($usercount)
{
    $amount = 1500;
    $min = 1;
    $max = 500;
    $number = 10;
    $invite_number = 10;
    for ($i = 1; $i < $number; $i++) {
        $safe_total = ($amount - ($number - $i) * $min) / ($number - $i);
        $money = round(mt_rand($min * 100, $safe_total * 100) / 100);
        $amount = round($amount - $money);
        $arr1[] = $money;
    }
    $arr1[] = $amount;
    rsort($arr1);

    $amount = 1000;
    $number = 30;
    for ($i = 1; $i < $number; $i++) {
        $safe_total = ($amount - ($number - $i) * $min) / ($number - $i);
        $money = round(mt_rand($min * 100, $safe_total * 100) / 100);
        $amount = round($amount - $money);
        $arr2[] = $money;
    }
    $arr2[] = $amount;
    rsort($arr2);


    if ($usercount >= $invite_number) {
        $money = end($arr2);
    } else {
        $money = $arr1[$usercount];
        if ($money > $max) {
            $money = invitationReward($usercount);
        }
    }
    return $money;
}

//通过区号获取国家信息
function getCountryByArea($area)
{
    $countries = json_decode(file_get_contents(public_path('country.json')), true);
    $countries = collect($countries);
    $country = $countries->where('areacode', '=', $area)->first();
    return $country['cn'] ?? '';
}

//通过国家获取区号
function getAreaByCountry($country)
{
    if (!$country) {
        return '0';
    }
    $countries = json_decode(file_get_contents(public_path('country.json')), true);
    $countries = collect($countries);
    $country = $countries->where('cn', '=', $country)->first();
    return $country['areacode'] ?? '0';
}

//获取随机区号
function getRandArea()
{
    $countries = json_decode(file_get_contents(public_path('country.json')), true);
    $countries = collect($countries);
    $country = $countries->random();
    return $country['areacode'] ?? '233';
}

//通过ip获取国家信息
function getCountryByIp($ip)
{
    try {
        $context = stream_context_create([
            'http' => ['timeout' => 3]
        ]);
        if (1) {  // 暂时只用1个平台
        //if (mt_rand(1, 10) % 2 === 0) {
            $ip = file_get_contents('http://ip-api.com/json/' . $ip . '?lang=zh-CN', 0, $context);
            $country = json_decode($ip, true);
            $country = $country['country'];
        } else {
            $ip = file_get_contents('http://www.geoplugin.net/json.gp?ip=' . $ip . '&lang=zh-CN', 0, $context);
            $country = json_decode($ip, true);
            $country = $country['geoplugin_countryName'];
        }
        return $country;
    } catch (Exception $exception) {
        return '';
    }
}

//字符串中变量解析
function stringParsingVariable($str, $array){
    $match = array();
    preg_match_all('/{\$(.*?)}/', $str, $match);
    foreach ($match[1] as $key => $value) {
        if (isset($array[$value])) {
            $str = str_replace($match[0][$key], $array[$value], $str);
        }elseif (Str::startsWith($value, 'rand')){
            $rand_arr = Str::of($value)->explode('_');
            $str = str_replace($match[0][$key], rand($rand_arr[1],$rand_arr[2]), $str);
        }
    }
    return $str;
}

/**
 * 判断是否FaceBook内部访问
 * @return bool 为true表示内部访问
 * @author danye.cc BerTang 2017-08-16
 */
function is_facebook(){
    $face = false;
    //来路域名判断
    if(isset($_SERVER['HTTP_REFERER']) && strpos($_SERVER['HTTP_REFERER'],'facebook.com')){
        $face = true;
    }
    //用户浏览器信息
    if(strpos($_SERVER["HTTP_USER_AGENT"], "facebookexternalhit/")||strpos($_SERVER["HTTP_USER_AGENT"], "Facebot")){
        $face = true;
    }
    //浏览器信息 正则方式判断
    if(preg_match('/^FacebookExternalHit\/.*?/i',$_SERVER["HTTP_USER_AGENT"])){
        $face = true;
    }
    //Messenger
    if(strpos($_SERVER["HTTP_USER_AGENT"], "FB")){
        $face = true;
    }

    return $face;
}

//取数组中倒数第N个元素
function getNthLastElement($array, $n) {
    // 使用array_slice获取倒数第N个元素
    // 参数-N指定从数组末尾开始取，0指定取一个元素，$n指定要跳过的元素数量
    $slice = array_slice($array, -$n, 1);
    // 如果找到元素，返回它，否则返回false
    return $slice ? reset($slice) : false;
}
